package com.dhcc.DRGsHosp.facade.impl;

import com.dhcc.DRGsHosp.application.DrgsIndexCalApplication;
import com.dhcc.DRGsHosp.application.JasperReportApplication;
import com.dhcc.DRGsHosp.facade.DrgsIndexCalFacade;
import com.dhcc.DRGsHosp.facade.JasperReportFacade;
import com.dhcc.DRGsHosp.facade.dto.IndexCalDTO;
import com.dhcc.DRGsHosp.infra.util.DrgsIndex;
import com.dhcc.DRGsHosp.infra.util.GlobalData;
import com.dhcc.DRGsHosp.infra.util.MonitorResultUtil;
import org.codehaus.jackson.map.ObjectMapper;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.util.JRLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.inject.Named;
import java.io.*;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.*;

/**
 * 
 * @ClassName: JasperReportFacadeImpl  
 * @Description ：    
 * @Date: 2017年9月2日 上午11:45:20  
 * @Author ChenHanlin 
 * @Version 2.5.0 DHC-DRGs
 */
@Named("drgsHospFacadeImpl20170902chl")
public class JasperReportFacadeImpl implements JasperReportFacade {
	@Autowired
	JasperReportApplication jasperReportApplication ;
	@Autowired
	private DrgsIndexCalFacade drgsIndexCalFacade ;
	@Autowired
	private JdbcTemplate jdbcTemplateDrgs;
	@Autowired
    private DrgsIndexCalApplication  drgsIndexCalApplication ;
	
	private static final Logger logger = LoggerFactory.getLogger(JasperReportFacadeImpl.class);

	@Override
	public List<Map<String, Object>> getJasperResult(String sql, Map<String, Object> params) {
		sql = sql.replaceAll("\\$P\\{", ":") ;
		sql = sql.replaceAll("\\$P!\\{", ":") ;
		sql = sql.replaceAll("\\}", " ") ;
		sql = sql.replaceAll(":dateField", params.get("dateField").toString()) ;
		return jasperReportApplication.executeQueryForList(sql, params) ;
	}
	
	@Override
	public List<Map<String, Object>> queryDepTree(String chkStyle,String layer) {
		String layerConfig = GlobalData.sysConfig.get("DEP_LINE_LAYER") ;
		String[] layerConfigArray = layerConfig == null?"".split(","):layerConfig.split(",") ;
		StringBuffer strb = new StringBuffer() ;
		strb.append(" select depgroup.depgroup_code,depgroup.depgroup_desc,ward.ward_code,ward.ward_desc,dep.dep_code,dep.dep_name from drgs.t_department dep \n ") ;
		strb.append(" left join drgs.t_depgroup depgroup  on depgroup.depgroup_code = dep.pardepgroup_code \n ") ;
		strb.append(" left join drgs.t_ward ward on ward.pardep_code = dep.dep_code \n ") ;
		strb.append(" where dep.DEP_TYPE = 'E' \n ") ;
		strb.append(" order by dep.dep_code ,ward.ward_code \n ") ;
		List<Map<String,Object>> resultList = jasperReportApplication.executeQueryForList(strb.toString(),new HashMap<String,Object>()) ;
		// 用于去除重复数据
		Set<Map<String,Object>> setMap = new HashSet<Map<String,Object>>(); 
		// 用于保存ztree节点
		List<Map<String,Object>> nodeList = new ArrayList<Map<String,Object>>() ;
		Map<String,Object> nodeAll = new HashMap<String,Object>() ;
		nodeAll.put("id", "allDep") ;
		nodeAll.put("pid","0") ;
		nodeAll.put("name","全部") ;
		nodeAll.put("nocheck","true");
		nodeList.add(nodeAll) ;
		// 用于存储默认选中中节点的Id
		String defDepCode = null ;
		String defWardCode = null ;
		for(Map<String,Object> node : resultList) {
			// 科室组节点
			Map<String,Object> nodeGroup = new HashMap<String,Object>() ;
			// 科室组编码
			Object depgroupCode = node.get("DEPGROUP_CODE") ;
			// 科室节点
			Map<String,Object> nodeDep = new HashMap<String,Object>() ;
			// 科室编码
			Object depCode = node.get("DEP_CODE") ;
			// 病区节点
			Map<String,Object> nodeWard = new HashMap<String,Object>() ;
			// 病区编码
			Object wardCode = node.get("WARD_CODE") ;
			
			// 科室组o
			if("1".equals(layerConfigArray[0])&& null!=depgroupCode){
				nodeGroup.put("id", "depGroup_"+depgroupCode) ;
				nodeGroup.put("name",node.get("DEPGROUP_DESC")) ;
				nodeGroup.put("pid","allDep") ;
				nodeGroup.put("nocheck","true");
				if("1".equals(layerConfigArray[0])) {
					if(setMap.add(nodeGroup)) {
						nodeList.add(nodeGroup) ;
					}
				}
			}
			
			
			// 科室
			nodeDep.put("id", depCode) ;
			nodeDep.put("name",node.get("DEP_NAME")) ;
			// 没有科室组将根节点作为父节点
			if("1".equals(layerConfigArray[0])&& null!=depgroupCode) {
				nodeDep.put("pid","depGroup_"+depgroupCode) ;
			} else {
				nodeDep.put("pid","allDep") ;
			}
			if("1".equals(layerConfigArray[1])) {
				if(setMap.add(nodeDep)) {
					nodeList.add(nodeDep) ;
				}
			}
			
			if("ward".equals(layer)) {
				// 病区
				nodeWard.put("id", wardCode) ;
				nodeWard.put("name",node.get("WARD_DESC")) ;
				// 判断上一层级是否存在
				if("1".equals(layerConfigArray[1])) {
					nodeWard.put("pid", depCode) ;
				} else if("1".equals(layerConfigArray[0])&& null!=depgroupCode)  {
					nodeWard.put("pid","depGroup_"+depgroupCode) ;
				} else {
					nodeWard.put("pid","allDep") ;
				}
				if("1".equals(layerConfigArray[2])) {
					if(setMap.add(nodeWard)) {
						nodeList.add(nodeWard) ;
					}
				}
			}
			
			
			//记录选中的第一个节点
			if("dep".equals(layer)) {
				if(defDepCode == null && "1".equals(layerConfigArray[1])) {
					defDepCode = depCode.toString() ;
				}
			} else if("ward".equals(layer)) {
				if(defWardCode == null && "1".equals(layerConfigArray[2])) {
					defWardCode = wardCode.toString() ;
				}
			}
			
		}
		
		
		// 选中并展开节点
		if("dep".equals(layer)) {
			checkAndExpendNode(nodeList,defDepCode,chkStyle) ;
		} else if("ward".equals(layer)) {
			checkAndExpendNode(nodeList,defWardCode,chkStyle) ;
		}
		return nodeList;
	}
	
	/**
	 * 设置节点的选中和展开状态
	 * @param nodeList
	 * @param id
	 * @param chkStyle
	 */
	private void checkAndExpendNode(List<Map<String,Object>> nodeList,String id,String chkStyle) {
		for(int i=0 ;i<nodeList.size(); i++) {
			if(nodeList.get(i).get("id").equals(id)) {
				nodeList.get(i).put("checked", "true") ;
				// 单选只选中子节点
				if("radio".equals(chkStyle)) {
					for(int j=i-1; j>=0; j--) {
						nodeList.get(j).put("open", "true") ;
						nodeList.get(j).put("nocheck", "true") ;
					}
				} else {
					for(int j=i-1; j>=0; j--) {
						nodeList.get(j).put("open", "true") ;
						nodeList.get(j).put("checked", "true") ;
					}
				}
				break ;
			}
		}
	}
	
	@Override
	public List<Map<String, Object>> findJasperReport(String reportId) {
		// 1 获取报表的配置信息 路径和文件名
		// 报表配置文件的properties对象
		Properties properties = null;
		InputStream propInputStream = null;
		propInputStream = JasperReportFacadeImpl.class.getResourceAsStream("/jasperTemplate.properties") ;
		properties = new Properties();
		try {
			properties.load(propInputStream);
		} catch (IOException e) {
			e.printStackTrace();
		} finally{
			if(null != propInputStream){
				try {
					propInputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	    // 如果reportId为空
	    if( reportId == null ){
		   reportId = "drgs" ;
	    } 
	    // 获取报表的路径和文件名
	    String[] reportIds = reportId.split(",") ;
	    List<Map<String, Object>> reportList = new ArrayList<>() ;
	    for(String repId : reportIds) {
	    	Map<String, Object> map = new HashMap<>() ;
		    String templatePath = (properties.getProperty(repId) !=null ? properties.getProperty(repId):"/WEB-INF/jasper/yizheng/drgs.jasper");
		    String fileName = (properties.getProperty(repId+"FN") !=null ? properties.getProperty(repId+"FN"):repId) ;
		    map.put("id", repId) ;
		    map.put("path", templatePath) ;
		    map.put("fileName", fileName) ;
		    reportList.add(map) ;
	    }
		return reportList ;
	}

	@Override
	public void processQueryParam(List<Map<String, Object>> reportList,String param,String reportType,String roleName) {
	    Map<String,Object> params ;
	    // 含有综合得分报表
	    List<String> listRep = Arrays.asList("yzDepgroup","yzDep","yzWard","yzPargroup");
		ObjectMapper objectMapper = new ObjectMapper();
	    try {
	    	// 将页面传来的sql参数转为map对象
	    	params = objectMapper.readValue(param, Map.class) ;
		} catch (Exception e) {
			params = new HashMap<String, Object>() ;
		}
	   // 获取子报表路径
	    String subReportDir = JasperReportFacadeImpl.class.getClassLoader().getResource("").getPath() ;
	    subReportDir = subReportDir.substring(0, subReportDir.lastIndexOf("classes/"))+"jasper/" ;
		if("xlsx".equals(reportType) || "html".equals(reportType)){
			params.put("IS_IGNORE_PAGINATION", true) ;
		}
	    params.put("SUBREPORT_DIR", subReportDir) ;
	    // 查询起止时间
	    //添加了qdate时间，该时间有年月，用于医保的指标查询，使用该时间的时候页面没有sdate和edate,所以根据qdate给sdate和edate一个值。
    	String qDate = params.get("qdate") == null ? null : params.get("qdate").toString() ;
    	String sDate = params.get("sdate") == null ? null : params.get("sdate").toString() ;
    	String eDate = params.get("edate") == null ? null : params.get("edate").toString() ;
    	if(qDate!=null&&sDate==null) {
    		sDate=qDate+"-01";
    	}
    	if(eDate == null) {
    		eDate = sDate ;
    	}
    	// 获取分层配置信息用于 产能得分的计算
    	String layerConfigs	 =GlobalData.sysConfig.get("DEP_LINE_LAYER");   	
    	// 获取分组方案
	    String verRegion = drgsIndexCalFacade.getGroupScheme() ;
	    params.put("verRegion", verRegion) ;
	    String stdId = drgsIndexCalFacade.getStdID(sDate, eDate) ;
	    params.put("stdId", stdId) ;
	    // 获取管理目标年份
	    IndexCalDTO index = new IndexCalDTO() ;
	    index.setDateTo(eDate);
	    index.setMsCode("manager_inner");
	    if("医保".equals(roleName)) {
	    	index.setpType("1");
	    } else {
	    	index.setpType("0");
	    }
	    String manageYear = drgsIndexCalFacade.getManageYear(index);
		params.put("manageYear", manageYear ) ;
		// 获取标杆年份
		index.setMsCode("std_inner");
		String stdYear = drgsIndexCalFacade.getManageYear(index);
		params.put("stdYear", stdYear ) ;
		// 根据分层配置信息获取当前层级的上一级，用于产能得分的计算
    	// SQL查询
		for(Map<String, Object> map : reportList) {
			// 含有综合得分的报表
			if(listRep.contains(map.get("id"))) {
				String before = "";
				String after = "";
				Map<String, Object> paramTemp = (Map<String, Object>) DrgsIndex.copyPropertiesObj(params) ;
				String[] stra = GlobalData.sysConfig.get("TOTALSCORE") == null? new String[]{"1","1","1"}:GlobalData.sysConfig.get("TOTALSCORE").toString().split("θ");
				for(int i=0;i<stra.length;i++){
					if(i==0){
						paramTemp.put("rc", MonitorResultUtil.objectToDouble(stra[i])) ;
						before +=" a.capScore * "+MonitorResultUtil.objectToDouble(stra[i]);
					}
					if(i==1){
						paramTemp.put("re", MonitorResultUtil.objectToDouble(stra[i])) ;
						before +=" + a.EffScore * "+MonitorResultUtil.objectToDouble(stra[i]);
					}
					if(i==2){
						paramTemp.put("rq", MonitorResultUtil.objectToDouble(stra[i])) ;
						before +=" + a.QuotaScore * "+MonitorResultUtil.objectToDouble(stra[i]);
					}
				}
				//非医保 则按综合得分降序排列
				if("0".equals(index.getpType()) && !"".equals(before) && before.length() >0 ){
					//排序
					before = " select t.* from( select a.*, round(" +before+",2) compositeScore from( ";
					after = " )a )t order by t.compositeScore desc ";
				}else{
					before = "";
					after = "";
				}
				//报表SQL
	    		String str = drgsIndexCalApplication.getSqlByQuotaCode("jasper_yizheng") ;
	    		String[] layerConfig = null ;
	    		if( null != layerConfigs) {
	    			layerConfig = layerConfigs.split(",") ;
	    		}
	    		// 默认为科室管理目标
	    		paramTemp.put("manageObjf", "dep.dep_code managedep,") ;
	    		paramTemp.put("manageObjfg", "") ;
	    		paramTemp.put("manageObja", "managedep") ;
    			String sql = new String(str);
    			// 报表id
    			switch(map.get("id")+"") {
	    			case "yzDepgroup":  {
    					// 1 代表全院
    					paramTemp.put("code","depgroup.depgroup_code") ;
    					paramTemp.put("desc","depgroup.depgroup_desc") ;
    					paramTemp.put("codeupper","1") ;
    					paramTemp.put("descupper","1") ;
    					// 管理目标关联科室组
    					paramTemp.put("manageobj", "pardepgroup_code");
    					paramTemp.put("manageCondition", "and b.pardep_code is null");
    					paramTemp.put("manageObjf", "") ;
    					paramTemp.put("manageObja", "ccode") ;
	    				break ;
	    			} 
    				case "yzDep":  {
    					// 科室的上一层级是全院
    					if(!"1".equals(layerConfig[0])) {
    						// 1 代表全院
    						paramTemp.put("code","dep.dep_code") ;
    						paramTemp.put("desc","dep.dep_name") ;
    						paramTemp.put("codeupper","1") ;
    						paramTemp.put("descupper","1") ;
    					} else { // 科室的上一层级是科室组
    						paramTemp.put("code","dep.dep_code") ;
    						paramTemp.put("desc","dep.dep_name") ;
    						paramTemp.put("codeupper","depgroup.depgroup_code") ;
    						paramTemp.put("descupper","depgroup.depgroup_desc") ;
    					}
    					paramTemp.put("manageobj", "pardep_code");
    					paramTemp.put("manageCondition", "and b.pardepgroup_code is null");
    					break ;
    				} 
    				case "yzWard":  {
    					if(!"1".equals(layerConfig[1])) {
    						paramTemp.put("code","ward.ward_code") ;
    						paramTemp.put("desc","ward.ward_desc") ;
    						paramTemp.put("codeupper","depgroup.depgroup_code") ;
    						paramTemp.put("descupper","depgroup.depgroup_desc") ;
    					} else {
    						paramTemp.put("code","ward.ward_code") ;
    						paramTemp.put("desc","ward.ward_desc") ;
    						paramTemp.put("codeupper","dep.dep_code") ;
    						paramTemp.put("descupper","dep.dep_name") ;
    					}
    					paramTemp.put("manageobj", "pardep_code");
    					paramTemp.put("manageCondition", "and b.pardepgroup_code is null");
    					break ;
    				} 
    				case "yzPargroup":  {
    					if(!"1".equals(layerConfig[2])) {
    						paramTemp.put("code","cg.group_code") ;
    						paramTemp.put("desc","cg.group_name") ;
    						paramTemp.put("codeupper","dep.dep_code") ;
    						paramTemp.put("descupper","dep.dep_name") ;
    					} else {
    						paramTemp.put("pargroupupperdesc", "病区") ;
    						paramTemp.put("code","cg.group_code") ;
    						paramTemp.put("desc","cg.group_name") ;
    						paramTemp.put("codeupper","ward.ward_code") ;
    						paramTemp.put("descupper","ward.ward_desc") ;
    						// 管理目标取科室
        					paramTemp.put("manageObjfg", ",dep.dep_code") ;
    					}
    					paramTemp.put("manageobj", "pardep_code");
    					paramTemp.put("manageCondition", "and b.pardepgroup_code is null");
    					
    					// 取消注释
    					sql = sql.replace("--left join drgs.t_clinicgroup cg on ipinfo.P00601001 = cg.group_code", "left join drgs.t_clinicgroup cg on ipinfo.P00601001 = cg.group_code");
    					break ;
    				} 
        		}
    			// SQL查询参数赋值
    	    	Iterator<Entry<String, Object>> ite = paramTemp.entrySet().iterator() ;
    			while(ite.hasNext()) {
    				Entry<String, Object> entry = ite.next() ;
    				// 字段替换
    				sql = sql.replace("$P!{"+entry.getKey()+"}", entry.getValue().toString()) ;
    				// 字段值替换-sql参数替换
    				sql = sql.replace("$P{"+entry.getKey()+"}", "'"+entry.getValue().toString()+"'") ;
    			}
    			sql = sql.replace("$P{before}", before) ;
    			sql = sql.replace("$P{after}", after) ;
    			paramTemp.put("sql",sql);
    			map.put("params", paramTemp) ;
			} else {
				// 没有综合得分的报表,不需增加参数
				map.put("params", params) ;
			}
		}
	}

	@Override
	public ArrayList<JasperPrint> getJasperPrint(final List<Map<String, Object>> reportList) {
		String pathTemp = JasperReportFacadeImpl.class.getClassLoader().getResource("").getPath() ;
		final String path = pathTemp.substring(0, pathTemp.lastIndexOf("/WEB-INF/classes/")) ;
		ArrayList<JasperPrint> sheets = new ArrayList<JasperPrint>();
		ExecutorService threadPool =  Executors.newFixedThreadPool(reportList.size());  
        CompletionService<JasperPrint> completionService = new ExecutorCompletionService<JasperPrint>(threadPool);
        try {
        	for(final Map<String, Object> reportMap : reportList) {
    			completionService.submit(new Callable<JasperPrint>(){
    				@Override
    				public JasperPrint call() {
    					// 构造jasper文件流
    					InputStream jasperStream = null ;
    					JasperReport jasperReport = null ;
						try {
							jasperStream = new FileInputStream(new File(path+reportMap.get("path")));
							 // 获取jasper模板文件
	    					jasperReport = (JasperReport) JRLoader.loadObject(jasperStream);
						} catch (FileNotFoundException e1) {
							e1.printStackTrace();
						} catch (JRException e) {
							e.printStackTrace();
						} finally {
							if(null != jasperStream){
								try {
									jasperStream.close();
								} catch (IOException e) {
									e.printStackTrace();
								}
							}
						};
    				   
    					// 填充模板
    					JasperPrint jasperPrint = null ;
    					Connection conn = null ;
    					try {
    						// 获取数据库连接
    						conn = jdbcTemplateDrgs.getDataSource().getConnection() ;
    						jasperPrint = JasperFillManager.fillReport(jasperReport, (Map<String,Object>)reportMap.get("params"),conn);
    						jasperPrint.setName(reportMap.get("fileName")+"");
    					} catch (Exception e) {
    						logger.error(e.getMessage());
    					}  finally {
    						if(null!=conn){
    							try {
									conn.close();
								} catch (SQLException e) {
									e.printStackTrace();
								}
        						conn = null ;
    						}
    						if(null!=jasperStream){
    							try {
									jasperStream.close();
								} catch (IOException e) {
									e.printStackTrace();
								}
    						}
    					}
    					return jasperPrint;
    				}
    			}) ;
    		}
    		for(Map<String, Object> reportMap:reportList) {
    			try {
    				sheets.add(completionService.take().get());
    			} catch (InterruptedException | ExecutionException e) {
    				logger.error(reportMap.get("fileName")+"报表报错了");
    				e.printStackTrace();
    			} 
    		}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			threadPool.shutdown();
		}
		return sheets;
	}
}
